/**
 * Copyright (C) 2009 - 2013 SC 4ViewSoft SRL
 * <p>
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * <p>
 * http://www.apache.org/licenses/LICENSE-2.0
 * <p>
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.android.settings.audio.peq.peq.achart.tools;

import java.util.ArrayList;
import java.util.List;

import com.android.settings.audio.peq.peq.achart.chart.AbstractChart;
import com.android.settings.audio.peq.peq.achart.chart.RoundChart;
import com.android.settings.audio.peq.peq.achart.chart.XYChart;

/**
 * The pan tool.
 */
public class Pan extends AbstractTool {
    /** The pan listeners. */
    private List<PanListener> mPanListeners = new ArrayList<PanListener>();
    /** Pan limits reached on the X axis. */
    private boolean limitsReachedX = false;
    /** Pan limits reached on the X axis. */
    private boolean limitsReachedY = false;

    /**
     * Builds and instance of the pan tool.
     *
     * @param chart the XY chart
     */
    public Pan(AbstractChart chart) {
        super(chart);
    }

    /**
     * Apply the tool.
     *
     * @param oldX the previous location on X axis
     * @param oldY the previous location on Y axis
     * @param newX the current location on X axis
     * @param newY the current location on the Y axis
     */
    public void apply(float oldX, float oldY, float newX, float newY) {
        boolean notLimitedUp = true;
        boolean notLimitedBottom = true;
        boolean notLimitedLeft = true;
        boolean notLimitedRight = true;
        if (mChart instanceof XYChart) {
            int scales = mRenderer.getScalesCount();
            double[] limits = mRenderer.getPanLimits();
            boolean limited = limits != null && limits.length == 4;
            XYChart chart = (XYChart) mChart;
            for (int i = 0; i < scales; i++) {
                double[] range = getRange(i);
                double[] calcRange = chart.getCalcRange(i);
                if (limitsReachedX
                        && limitsReachedY
                        && (range[0] == range[1] && calcRange[0] == calcRange[1] || range[2] == range[3]
                        && calcRange[2] == calcRange[3])) {
                    return;
                }
                checkRange(range, i);

                double[] realPoint = chart.toRealPoint(oldX, oldY, i);
                double[] realPoint2 = chart.toRealPoint(newX, newY, i);
                double deltaX = realPoint[0] - realPoint2[0];
                double deltaY = realPoint[1] - realPoint2[1];
                double ratio = getAxisRatio(range);
                if (chart.isVertical(mRenderer)) {
                    double newDeltaX = -deltaY * ratio;
                    double newDeltaY = deltaX / ratio;
                    deltaX = newDeltaX;
                    deltaY = newDeltaY;
                }
                if (mRenderer.isPanXEnabled()) {
                    if (limits != null) {
                        if (notLimitedLeft) {
                            notLimitedLeft = limits[0] <= range[0] + deltaX;
                        }
                        if (notLimitedRight) {
                            notLimitedRight = limits[1] >= range[1] + deltaX;
                        }
                    }
                    if (!limited || (notLimitedLeft && notLimitedRight)) {
                        setXRange(range[0] + deltaX, range[1] + deltaX, i);
                        limitsReachedX = false;
                    } else {
                        limitsReachedX = true;
                    }
                }
                if (mRenderer.isPanYEnabled()) {
                    if (limits != null) {
                        if (notLimitedBottom) {
                            notLimitedBottom = limits[2] <= range[2] + deltaY;
                        }
                        if (notLimitedUp) {
                            notLimitedUp = limits[3] >= range[3] + deltaY;
                        }
                    }
                    if (!limited || (notLimitedBottom && notLimitedUp)) {
                        setYRange(range[2] + deltaY, range[3] + deltaY, i);
                        limitsReachedY = false;
                    } else {
                        limitsReachedY = true;
                    }
                }
            }
        } else {
            RoundChart chart = (RoundChart) mChart;
            chart.setCenterX(chart.getCenterX() + (int) (newX - oldX));
            chart.setCenterY(chart.getCenterY() + (int) (newY - oldY));
        }
        notifyPanListeners();
    }

    /**
     * Return the X / Y axis range ratio.
     *
     * @param range the axis range
     * @return the ratio
     */
    private double getAxisRatio(double[] range) {
        return Math.abs(range[1] - range[0]) / Math.abs(range[3] - range[2]);
    }

    /**
     * Notify the pan listeners about a pan.
     */
    private synchronized void notifyPanListeners() {
        for (PanListener listener : mPanListeners) {
            listener.panApplied();
        }
    }

    /**
     * Adds a new pan listener.
     *
     * @param listener pan listener
     */
    public synchronized void addPanListener(PanListener listener) {
        mPanListeners.add(listener);
    }

    /**
     * Removes a pan listener.
     *
     * @param listener pan listener
     */
    public synchronized void removePanListener(PanListener listener) {
        mPanListeners.remove(listener);
    }

}
